home *** CD-ROM | disk | FTP | other *** search
- {
- I've gotten tired of writing these routines and have gone on to other
- projects so I don't have time to work on them now. I figured others may get
- some use out of them though. They're not totally done yet, but what is there
- does work (as far as I can tell). They support playing digitized Sound
- (signed or unsigned) at sample rates from 18hz to 44.1khz (at least on my
- 386sx/25), on the PC Speaker (polled), LPT DACs (1-4) or Adlib FM channels. I
- was planning on adding Sound Blaster DAC, Gravis UltraSound, and PC Speaker
- (pulse width modulated) support. I also planned on adding VOC support. I
- may add those at a later date, but no promises. I'll release any new updates
- (if there are any) through the PDN since these routines are a little long
- (this will be the ONLY post of these routines in this echo). I haven't
- tested the LPT DAC routines, so could someone who has an LPT DAC please test
- them and let me know if they work? (They SHOULD work, but you never know.)
- These routines work For me under Turbo Pascal V6.0 on my 386sx/25.
- }
-
- Unit Digital;
- (*************************************************************************)
- (* *)
- (* Programmed by David Dahl *)
- (* This Unit and all routines are PUBLIC DOMAIN. *)
- (* *)
- (* Special thanks to Emil Gilliam For information (and code!) on Adlib *)
- (* digital output. *)
- (* *)
- (* if you use any of these routines in your own Programs, I would *)
- (* appreciate an acknowledgement in the docs and/or Program... and I'm *)
- (* sure Mr. Gilliam wouldn't Object to having his name mentioned, too. *)
- (* *)
- (*************************************************************************)
- Interface
-
- Const
- BufSize = 2048;
-
- Type
- BufferType = Array[1 .. BufSize] of Byte;
- BufPointer = ^BufferType;
-
- DeviceType = (LPT1, LPT2, LPT3, LPT4, PcSpeaker, PCSpeakPW, Adlib,
- SoundBlaster, UltraSound);
-
- Var
- DonePlaying : Boolean;
-
- Procedure SetOutPutDevice(DeviceName : DeviceType; SignedSamples : Boolean);
- Procedure SetPlaySpeed(Speed : LongInt);
-
- Procedure PlayRAWSoundFile(FileName : String; SampleRate : Word);
- Function LoadBuffer(Var F : File; Var BufP : BufPointer) : Word;
- Procedure PlayBuffer(BufPtr : BufPointer; Size : Word);
-
- Procedure HaltPlaying;
- Procedure CleanUp;
-
- Implementation
-
- Uses
- Crt;
-
- Const
- C8253ModeControl = $43;
- C8253Channel : Array [0..2] of Byte = ($40, $41, $42);
- C8253OperatingFreq = 1193180;
- C8259Command = $20;
-
- TimerInterrupt = $08;
- AdlibIndex = $388;
- AdlibReg = $389;
-
- Type
- ZeroAndOne = 0..1;
-
- Var
- DataLength : Word;
- Buffer : BufPointer;
-
- LPTAddress : Word;
- LPTPort : Array [1 .. 4] of Word Absolute $0040 : $0008;
-
- OldTimerInterrupt : Pointer;
- InterruptVector : Array [0..255] of Pointer Absolute $0000 : $0000;
-
- {=[ Misc Procedures ]=====================================================}
-
- {-[ Clear Interrupt Flag (Disable Maskable Interrupts) ]------------------}
- Procedure CLI;
- Inline($FA);
-
- {-[ Set Interrupt Flag ]--------------------------------------------------}
- Procedure STI;
- Inline($FB);
-
-
- {=[ Initialize Sound Devices ]============================================}
-
- {-[ Initialize Adlib FM For Digital Output ]------------------------------}
- Procedure InitializeAdlib;
- Var
- TempInt : Pointer;
-
- Procedure Adlib(Reg, Data : Byte); Assembler;
- Asm
- mov dx, AdlibIndex { Adlib index port }
- mov al, Reg
-
- out dx,al { Set the index }
-
- { Wait For hardware to respond }
- in al, dx; in al, dx; in al, dx
- in al, dx; in al, dx; in al, dx
-
- inc dx { Adlib register port }
- mov al, Data
- out dx, al { Set the register value }
-
- dec dx { Adlib index port }
-
- { Wait For hardware to respond }
- in al, dx; in al, dx; in al, dx; in al, dx; in al, dx
- in al, dx; in al, dx; in al, dx; in al, dx; in al, dx
- in al, dx; in al, dx; in al, dx; in al, dx; in al, dx
- in al, dx; in al, dx; in al, dx; in al, dx; in al, dx
- in al, dx; in al, dx; in al, dx; in al, dx; in al, dx
- in al, dx; in al, dx; in al, dx; in al, dx; in al, dx
- in al, dx; in al, dx; in al, dx; in al, dx; in al, dx
-
- end;
-
- begin
- Adlib($00, $00); { Set Adlib test Register }
- Adlib($20, $21); { Operator 0: MULTI=1, AM=VIB=KSR=0, EG=1 }
- Adlib($60, $F0); { Attack = 15, Decay = 0 }
- Adlib($80, $F0); { Sustain = 15, Release = 0 }
- Adlib($C0, $01); { Feedback = 0, Additive Synthesis = 1 }
- Adlib($E0, $00); { Waveform = Sine Wave }
- Adlib($43, $3F); { Operator 4: Total Level = 63, Attenuation = 0 }
- Adlib($B0, $01); { Fnumber = 399 }
- Adlib($A0, $8F);
- Adlib($B0, $2E); { FNumber = 143, Key-On }
-
- { Wait For the operator's sine wave to get to top and then stop it there
- That way, we have an operator who's wave is stuck at the top, and we can
- play digitized Sound by changing it's total level (volume) register. }
-
- Asm
- mov al, 0 { Get timer 0 value into DX }
- out 43h, al
- jmp @Delay1
-
- @Delay1:
- in al, 40h
- mov dl, al
- jmp @Delay2
-
- @Delay2:
- in al, 40h
-
- mov dh, al
- sub dx, 952h { Target value }
-
- @wait_loop:
- mov al, 0 { Get timer 0 value into BX }
- out 43h, al
- jmp @Delay3
-
- @Delay3:
- in al, 40h
- mov bl, al
- jmp @Delay4
-
- @Delay4:
- in al, 40h
- mov bh, al
- cmp bx, dx { Have we waited that much time yet? }
- ja @wait_loop { if no, then go back }
-
- end;
-
- { Now that the sine wave is at the top, change its frequency to 0 to keep
- it from moving }
-
- Adlib($B0, $20); { F-Number = 0 }
- Adlib($A0, $00); { Frequency = 0 }
-
- Port[AdlibIndex] := $40;
- end;
-
- {=[ Sound Device Handlers ]===============================================}
- Procedure PlayPCSpeaker; Interrupt;
- Const
- Counter : Word = 1;
- begin
- if Not(DonePlaying) Then
- begin
- if Counter <= DataLength Then
- begin
- Port[$61] := (Port[$61] and 253) OR ((Buffer^[Counter] and 128) SHR 6);
- Inc(Counter);
- end
- else
- begin
- DonePlaying := True;
- Counter := 1;
- end;
- end;
-
- Port[C8259Command] := $20; { Enable Interrupts }
- end;
-
- Procedure PlayPCSpeakerSigned; Interrupt;
- Const
- Counter : Word = 1;
- begin
- if Not(DonePlaying) Then
- begin
- if Counter <= DataLength Then
- begin
- Port[$61] := (Port[$61] and 253) OR
- ((Byte(shortint(Buffer^[Counter]) + 128) AND 128) SHR 6);
- Inc(Counter);
- end
- else
- begin
- DonePlaying := True;
- Counter := 1;
- end;
- end;
-
- Port[C8259Command] := $20; { Enable Interrupts }
- end;
-
- Procedure PlayLPT; Interrupt;
- Const
- Counter : Word = 1;
- begin
- if Not(DonePlaying) Then
- begin
- if Counter <= DataLength Then
- begin
- Port[LPTAddress] := Buffer^[Counter];
- Inc(Counter);
- end
- else
- begin
- DonePlaying := True;
- Counter := 1;
- end;
- end;
-
- Port[C8259Command] := $20; { Enable Interupts }
- end;
-
- Procedure PlayLPTSigned; Interrupt;
- Const
- Counter : Word = 1;
- begin
- if Not(DonePlaying) Then
- begin
- if Counter <= DataLength Then
- begin
- Port[LPTAddress] := Byte(shortint(Buffer^[Counter]) + 128);
- Inc(Counter);
- end
- else
- begin
- DonePlaying := True;
- Counter := 1;
- end;
- end;
-
- Port[C8259Command] := $20; { Enable Interupts }
- end;
-
- Procedure PlayAdlib; Interrupt;
- Const
- Counter : Word = 1;
- begin
- if Not(DonePlaying) Then
- begin
- if Counter <= DataLength Then
- begin
- Port[AdlibReg] := (Buffer^[Counter] SHR 2);
- Inc(Counter);
- end
- else
- begin
- DonePlaying := True;
- Counter := 1;
- end;
- end;
-
- Port[C8259Command] := $20; { Enable Interupts }
- end;
-
- Procedure PlayAdlibSigned; Interrupt;
- Const
- Counter : Word = 1;
- begin
- if Not(DonePlaying) Then
- begin
- if Counter <= DataLength Then
- begin
- Port[AdlibReg] := Byte(shortint(Buffer^[Counter]) + 128) SHR 2;
- Inc(Counter);
- end
- else
- begin
- DonePlaying := True;
- Counter := 1;
- end;
- end;
-
- Port[C8259Command] := $20; { Enable Interupts }
- end;
-
- {=[ 8253 Timer Programming Routines ]=====================================}
- Procedure Set8253Channel(ChannelNumber : Byte; ProgramValue : Word);
- begin
- Port[C8253ModeControl] := 54 or (ChannelNumber SHL 6); { XX110110 }
- Port[C8253Channel[ChannelNumber]] := Lo(ProgramValue);
- Port[C8253Channel[ChannelNumber]] := Hi(ProgramValue);
- end;
-
- {-[ Set Clock Channel 0 (INT 8, IRQ 0) To Input Speed ]-------------------}
- Procedure SetPlaySpeed(Speed : LongInt);
- Var
- ProgramValue : Word;
- begin
- ProgramValue := C8253OperatingFreq div Speed;
- Set8253Channel(0, ProgramValue);
- end;
-
- {-[ Set Clock Channel 0 Back To 18.2 Default Value ]----------------------}
- Procedure SetDefaultTimerSpeed;
- begin
- Set8253Channel (0, 0);
- end;
-
-
- {=[ File Handling ]=======================================================}
-
- {-[ Load Buffer With Data From Raw File ]---------------------------------}
- Function LoadBuffer(Var F : File; Var BufP : BufPointer) : Word;
- Var
- NumRead : Word;
- begin
- BlockRead(F, BufP^, BufSize, NumRead);
- LoadBuffer := NumRead;
- end;
-
-
- {=[ Sound Playing / Setup Routines ]======================================}
-
- {-[ Output Sound Data In Buffer ]-----------------------------------------}
- Procedure PlayBuffer(BufPtr : BufPointer; Size : Word);
- begin
- Buffer := BufPtr;
- DataLength := Size;
- DonePlaying := False;
- end;
-
- {-[ Halt Playing ]--------------------------------------------------------}
- Procedure HaltPlaying;
- begin
- DonePlaying := True;
- end;
-
- {=[ Initialize Data ]=====================================================}
- Procedure InitializeData;
- Const
- CalledOnce : Boolean = False;
- begin
- if Not(CalledOnce) Then
- begin
- DonePlaying := True;
- OldTimerInterrupt := InterruptVector[TimerInterrupt];
- CalledOnce := True;
- end;
- end;
-
- {=[ Set Interrupt Vectors ]===============================================}
-
- {-[ Set Timer Interrupt Vector To Our Device ]----------------------------}
- Procedure SetOutPutDevice(DeviceName : DeviceType; SignedSamples : Boolean);
- begin
- CLI;
-
- Case DeviceName of
-
- LPT1..LPT4 :
- begin
- LPTAddress := LPTPort[Ord(DeviceName)];
- if SignedSamples Then
- InterruptVector[TimerInterrupt] := @PlayLPTSigned
- else
- InterruptVector[TimerInterrupt] := @PlayLPT;
- end;
-
- PCSpeaker :
- if SignedSamples Then
- InterruptVector[TimerInterrupt] := @PlayPCSpeakerSigned
- else
- InterruptVector[TimerInterrupt] := @PlayPCSpeaker;
-
- Adlib :
- begin
- InitializeAdlib;
- if SignedSamples Then
- InterruptVector[TimerInterrupt] := @PlayAdlibSigned
- else
- InterruptVector[TimerInterrupt] := @PlayAdlib;
- end;
-
- else
- begin
- STI;
-
- Writeln;
- Writeln ('That Sound Device Is Not Supported In This Version.');
- Writeln ('Using PC Speaker In Polled Mode Instead.');
-
- CLI;
- if SignedSamples Then
- InterruptVector[TimerInterrupt] := @PlayPCSpeakerSigned
- else
- InterruptVector[TimerInterrupt] := @PlayPCSpeaker;
- end;
- end;
- STI;
- end;
-
- {-[ Set Timer Interupt Vector To Default Handler ]------------------------}
- Procedure SetTimerInterruptVectorDefault;
- begin
- CLI;
- InterruptVector[TimerInterrupt] := OldTimerInterrupt;
- STI;
- end;
-
- Procedure PlayRAWSoundFile(FileName : String; SampleRate : Word);
- Var
- RawDataFile : File;
- SoundBuffer : Array [ZeroAndOne] of BufPointer;
- BufNum : ZeroAndOne;
- Size : Word;
- begin
- New(SoundBuffer[0]);
- New(SoundBuffer[1]);
-
- SetPlaySpeed(SampleRate);
-
- Assign(RawDataFile, FileName);
- Reset(RawDataFile, 1);
-
- BufNum := 0;
- Size := LoadBuffer(RawDataFile, SoundBuffer[BufNum]);
-
- PlayBuffer(SoundBuffer[BufNum], Size);
-
- While Not(Eof(RawDataFile)) do
- begin
- BufNum := (BufNum + 1) and 1;
- Size := LoadBuffer(RawDataFile, SoundBuffer[BufNum]);
-
- Repeat Until DonePlaying;
-
- PlayBuffer(SoundBuffer[BufNum], Size);
- end;
-
- Close (RawDataFile);
-
- Repeat Until DonePlaying;
-
- SetDefaultTimerSpeed;
-
- Dispose(SoundBuffer[1]);
- Dispose(SoundBuffer[0]);
- end;
-
- {=[ MUST CALL BEFORE ExitING Program!!! ]=================================}
- Procedure CleanUp;
- begin
- SetDefaultTimerSpeed;
- SetTimerInterruptVectorDefault;
- end;
-
- {=[ Set Up ]==============================================================}
- begin
- InitializeData;
- NoSound;
- end.
-
-
-
-
-
-
-
- Program RAWDigitalOutput;
-
- (*************************************************************************)
- (* *)
- (* Programmed by David Dahl *)
- (* This Program and all routines are PUBLIC DOMAIN. *)
- (* *)
- (* if you use any of these routines in your own Programs, I would *)
- (* appreciate an acknowledgement in the docs and/or Program. *)
- (* *)
- (*************************************************************************)
-
- Uses
- Crt,
- Digital;
-
- Type
- String4 = String[4];
- String35 = String[35];
-
- Const
- MaxDevices = 9;
-
- DeviceCommand : Array [1..MaxDevices] of String4 =
- ('-L1', '-L2', '-L3', '-L4',
- '-P' , '-PM', '-A' , '-SB', '-GUS' );
-
- DeviceName : Array [1..MaxDevices] of String35 =
- ('LPT DAC on LPT1',
- 'LPT DAC on LPT2',
- 'LPT DAC on LPT3',
- 'LPT DAC on LPT4',
- 'PC Speaker (Polled Mode)',
- 'PC Speaker (Pulse Width Modulated)',
- 'Adlib / SoundBlaster FM',
- 'SoundBlaster DAC',
- 'Gravis UltraSound');
-
- SignedUnsigned : Array [False .. True] of String35 =
- ('Unsigned Sample', 'Signed Sample');
-
-
- {-[ Return An All Capaitalized String ]-----------------------------------}
- Function UpString(StringIn : String) : String;
- Var
- TempString : String;
- Counter : Byte;
- begin
- TempString := '';
- For Counter := 1 to Length (StringIn) do
- TempString := TempString + UpCase(StringIn[Counter]);
-
- UpString := TempString;
- end;
-
- {-[ Check if File Exists ]------------------------------------------------}
- Function FileExists(FileName : String) : Boolean;
- Var
- F : File;
- begin
- {$I-}
- Assign (F, FileName);
- Reset(F);
- Close(F);
- {$I+}
- FileExists := (IOResult = 0) And (FileName <> '');
- end;
-
- {=[ Comand Line Parameter Decode ]========================================}
- Function FindOutPutDevice : DeviceType;
- Var
- Counter : Byte;
- DeviceCounter : Byte;
- Found : Boolean;
- Device : DeviceType;
- begin
- Counter := 1;
- Found := False;
- Device := PcSpeaker;
-
- While (Counter <= ParamCount) and Not(Found) do
- begin
- For DeviceCounter := 1 To MaxDevices do
- if UpString(ParamStr(Counter)) = DeviceCommand[DeviceCounter] Then
- begin
- Device := DeviceType(DeviceCounter - 1);
- Found := True;
- end;
-
- Inc(Counter);
- end;
-
- FindOutPutDevice := Device;
- end;
-
- Function FindRawFileName : String;
- Var
- FileNameFound : String;
- TempName : String;
- Found : Boolean;
- Counter : Byte;
- begin
- FileNameFound := '';
- Counter := 1;
- Found := False;
-
- While (Counter <= ParamCount) and Not(Found) do
- begin
- TempName := UpString(ParamStr(Counter));
- if TempName[1] <> '-' Then
- begin
- FileNameFound := TempName;
- Found := True;
- end;
- Inc (Counter);
- end;
-
- FindRawFileName := FileNameFound;
- end;
-
- Function FindPlayBackRate : Word;
- Var
- RateString : String;
- Rate : Word;
- Found : Boolean;
- Counter : Byte;
- ErrorCode : Integer;
- begin
- Rate := 22000;
- Counter := 1;
- Found := False;
-
- While (Counter <= ParamCount) and Not(Found) do
- begin
- RateString := UpString(ParamStr(Counter));
- if Copy(RateString,1,2) = '-F' Then
- begin
- RateString := Copy(RateString, 3, Length(RateString) - 2);
- Val(RateString, Rate, ErrorCode);
- if ErrorCode <> 0 Then
- begin
- Rate := 22000;
- Writeln ('Error In Frequency. Using Default');
- end;
- Found := True;
- end;
- Inc (Counter);
- end;
-
- if Rate < 18 Then
- Rate := 18
- else
- if Rate > 44100 Then
- Rate := 44100;
-
- FindPlayBackRate := Rate;
- end;
-
- Function SignedSample : Boolean;
- Var
- Found : Boolean;
- Counter : Word;
- begin
- SignedSample := False;
- Found := False;
- Counter := 1;
-
- While (Counter <= ParamCount) and Not(Found) do
- begin
- if UpString(ParamStr(Counter)) = '-S' Then
- begin
- SignedSample := True;
- Found := True;
- end;
-
- Inc(Counter);
- end;
- end;
-
- {=[ Main Program ]========================================================}
- Var
- SampleName : String;
- SampleRate : Word;
- OutDevice : DeviceType;
- begin
- Writeln;
- Writeln('RAW Sound File Player V0.07');
- Writeln('Programmed By David Dahl');
- Writeln('Thanks to Emil Gilliam For Adlib digital output information');
- Writeln('This Program is PUBLIC DOMAIN');
-
- if ParamCount <> 0 Then
- begin
- SampleRate := FindPlayBackRate;
- SampleName := FindRawFileName;
- OutDevice := FindOutPutDevice;
- Writeln;
-
- if SampleName <> '' Then
- begin
- Writeln('Raw File : ',SampleName);
- Writeln('Format : ',SignedUnsigned[SignedSample]);
- Writeln('Sample Rate: ',SampleRate);
- Writeln('Device : ',DeviceName[Ord(OutDevice)+1]);
-
- if FileExists(SampleName) Then
- begin
- SetOutputDevice(OutDevice, SignedSample);
- PlayRAWSoundFile(SampleName, SampleRate);
- end
- else
- Writeln('Sound File Not Found.');
- end
- else
- Writeln('Filename Not Specified.');
- end
- else
- begin
- Writeln;
- Writeln('USAGE:');
- Writeln(ParamStr(0),' [SWITCHES] <RAW DATA File>');
- Writeln;
- Writeln('SWITCHES:');
- Writeln(' -P PC Speaker, Polled (Default)');
- Writeln(' -L1 LPT DAC on LPT 1');
- Writeln(' -L2 LPT DAC on LPT 2');
- Writeln(' -L3 LPT DAC on LPT 3');
- Writeln(' -L4 LPT DAC on LPT 4');
- Writeln(' -A Adlib/Sound Blaster FM');
- Writeln;
- Writeln(' -S Signed Sample (Unsigned Default)');
- Writeln;
- Writeln(' -FXXXXX Frequency Of Sample. XXXXX can be any Integer ',
- 'between 18 to 44100');
- Writeln (' (22000 Default)');
- end;
-
- CleanUp;
- end.
-
-